# !/usr/bin/env python3
# -*- coding: utf-8 -*-


import math
import xml.etree.ElementTree as EleTree
from typing import List, Dict, Union, Any
from util.lib import pretty_xml
from util.topo_adapter import get_time_slot_len


def compute_hyper_period(flow_xml,
                         time_slot_len: int) -> int:
    """
    从flow_feature.xml中获取调度周期，调度周期要以时间槽为单位
    """
    period_set = []
    tree = EleTree.parse(flow_xml)
    # 这里获取的周期只包含ST流的周期，AVB流没有周期这一参数
    period_ele_set = tree.iter('period')
    for period_ele in period_ele_set:
        period_set.append(int(period_ele.text))

    # 计算流的调度周期
    hyper_period = 1
    for period in period_set:
        hyper_period = int(period) * int(hyper_period) / math.gcd(int(period), int(hyper_period))
        hyper_period = int(hyper_period)

    # 调度周期要转换成时间槽的倍数
    hyper_period = math.ceil(hyper_period / time_slot_len)

    return hyper_period


def init_para_set(para_xml,
                  hyper_period: int,
                  time_slot_len: int) -> List[Dict[Union[str, Any], int]]:
    para_set = []
    tree = EleTree.parse(para_xml)
    node_ele_set = tree.findall('node')
    for node_ele in node_ele_set:
        node_ele_list = list(node_ele)
        para_dic = {}
        for para in node_ele_list:
            if para.tag != 'cycle_start':
                if para.text[0:2] == '0x':
                    text = int(para.text, 16)
                else:
                    text = int(para.text)
                para_dic.update({para.tag: text})
        para_dic.update({'schedule_period': hyper_period})
        # cyclen以纳秒为单位
        para_dic.update({'cycle_start_cyclen': hyper_period * time_slot_len * 1000})
        para_dic.update({'time_slot_length': time_slot_len})

        para_set.append(para_dic)

    return para_set


def _init_para_tbl_xml(para_set, para_tbl_xml):
    root = EleTree.Element('para_tbl')
    for entry in para_set:
        node_ele = EleTree.SubElement(root, 'node')
        node_id_ele = EleTree.SubElement(node_ele, 'node_id')
        node_id_ele.text = str(entry['node_id'])
        node_type = EleTree.SubElement(node_ele, 'node_type')
        node_type.text = str(entry['node_type'])
        schedule_period = EleTree.SubElement(node_ele, 'schedule_period')
        schedule_period.text = str(entry['schedule_period'])
        cyclelen = EleTree.SubElement(node_ele, 'cyclelen')
        cyclelen.text = str(entry['cycle_start_cyclen'])
        time_slot_len = EleTree.SubElement(node_ele, 'time_slot_len')
        time_slot_len.text = str(entry['time_slot_length'])
        if entry['node_type'] == 0 or entry['node_type'] == 1:
            rc_threshold = EleTree.SubElement(node_ele, 'rc_threshold')
            rc_threshold.text = str(entry['rc_threshold'])
            hpriority_policing_threshold = EleTree.SubElement(node_ele, 'hpriority_policing_threshold')
            hpriority_policing_threshold.text = str(entry['hpriority_policing_threshold'])
            lpriority_policing_threshold = EleTree.SubElement(node_ele, 'lpriority_policing_threshold')
            lpriority_policing_threshold.text = str(entry['lpriority_policing_threshold'])
            qbv_or_qch = EleTree.SubElement(node_ele, 'qbv_or_qch')
            qbv_or_qch.text = str(entry['qbv_or_qch'])
        if entry['node_type'] == 0:
            committed_burst_size = EleTree.SubElement(node_ele, 'committed_burst_size')
            committed_burst_size.text = str(entry['committed_burst_size'])
            committed_information_rate = EleTree.SubElement(node_ele, 'committed_information_rate')
            committed_information_rate.text = str(entry['committed_information_rate'])
            # forward_mode = EleTree.SubElement(node_ele, 'forward_mode')
            # forward_mode.text = str(entry['forward_mode'])
    tree = EleTree.ElementTree(root)
    pretty_xml(root, '\t', '\n')
    tree.write(para_tbl_xml,
               encoding='utf-8',
               xml_declaration=True)
    return


def assemble_para_tbl(para_xml,
                      flow_xml,
                      topo_xml,
                      para_tbl_xml):

    # 1. 从topo_xml中获取时间槽长度
    time_slot_len = get_time_slot_len(topo_xml)
    print("1. time_slot_len: ", time_slot_len)
    # 2. 从flow_feature.xml中获取调度周期，调度周期要以时间槽为单位
    hyper_period = compute_hyper_period(flow_xml, time_slot_len)
    print("2. hyper_period: ", hyper_period)

    # 3. 从para_xml中获取除了调度周期以外的其他字段
    para_set = init_para_set(para_xml,
                             hyper_period,
                             time_slot_len)
    print("3. para_set: ")
    [print(entry) for entry in para_set]

    # 生成xml
    _init_para_tbl_xml(para_set, para_tbl_xml)
    return


def main():
    file_path = 'C:/Users/liuyalan/Desktop/OpenTSN_tool_chain/input/'
    para_xml = file_path + 'cfg_parameter.xml'
    flow_xml = file_path + 'flow_feature.xml'
    topo_xml = file_path + 'topo_feature.xml'
    para_tbl_xml = 'C:/Users/liuyalan/Desktop/OpenTSN_tool_chain/output/tbl/para_tbl.xml'

    assemble_para_tbl(para_xml,
                      flow_xml,
                      topo_xml,
                      para_tbl_xml)
    return


if __name__ == '__main__':
    main()


